package com.springboot.jwt.config;


import com.springboot.jwt.interfaces.JwtToken;
import com.springboot.jwt.error.JwtException;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import java.lang.reflect.Method;
import java.util.Arrays;

/**
 * @version 1.0
 * @ClassName AuthenticationInterceptor
 * @Description HandlerInterceptor接口主要定义了三个方法
 * 1.boolean preHandle ()：
 * 预处理回调方法,实现处理器的预处理，第三个参数为响应的处理器,自定义Controller,返回值为true表示继续流程（如调用下一个拦截器或处理器）或者接着执行
 * postHandle()和afterCompletion()；false表示流程中断，不会继续调用其他的拦截器或处理器，中断执行。
 * <p>
 * 2.void postHandle()：
 * 后处理回调方法，实现处理器的后处理（DispatcherServlet进行视图返回渲染之前进行调用），此时我们可以通过modelAndView（模型和视图对象）对模型数据进行处理或对视图进行处理，modelAndView也可能为null。
 * <p>
 * 3.void afterCompletion():
 * 整个请求处理完毕回调方法,该方法也是需要当前对应的Interceptor的preHandle()的返回值为true时才会执行，也就是在DispatcherServlet渲染了对应的视图之后执行。用于进行资源清理。整个请求处理完毕回调方法。如性能监控中我们可以在此记录结束时间并输出消耗时间，还可以进行一些资源清理，类似于try-catch-finally中的finally，但仅调用处理器执行链中
 * @Author zyj
 * @date 2021/3/30 18:17
 **/
@Slf4j
public class AuthenticationInterceptor implements HandlerInterceptor {



    @Value("${project.token_pass_url:}")
    private String[] tokenPassUrl;

    @Resource
    private JwtUtil jwtUtil;

    private String jwt_tishi = "token无效，请重新登录";
    private String jwt_tishi_code = "401";
    private String jwt_tishi_not = "不需要token";
    private String jwt_tishi_not_code = "200";

    @Override
    public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object object) throws Exception {
        String token = httpServletRequest.getHeader("token");// 从 http 请求头中取出 token
        log.info("验证签名-token:{}", token);

        System.out.println(httpServletRequest.getRequestURI());
        //step1: 如果不是映射到方法直接通过
        if (!(object instanceof HandlerMethod)) {
            return true;
        }

        //过滤指定的地址
        if(tokenPassUrl.length>1 ){
            String requestURI = httpServletRequest.getRequestURI();
            if(Arrays.asList(tokenPassUrl).contains(requestURI)){
                return true;
            }
            for(String url:tokenPassUrl){
                if(requestURI.startsWith(url)){
                    return true;
                }
            }
        }

        //step2: 检查是否有passtoken注释，有则跳过认证
        HandlerMethod handlerMethod = (HandlerMethod) object;
        JwtToken jwtToken = null;
        try {
            jwtToken = checkToken(handlerMethod);
        }catch (TokenException e){
            return true;
        }

        //step3: 检查有没有需要用户权限的注解
        // 执行认证
        if (StringUtils.isEmpty(token) || "null".equals(token)) {
            throw new JwtException(jwt_tishi);
        }


        //校验不同token
        if(!ObjectUtils.isEmpty(jwtToken) ){
            jwtUtil.getCheckTokenType(jwtToken.requestTokenType().getName()).checkToken(httpServletRequest, token);
        }

        //校验接口权限


        return true;
    }

    @Override
    public void postHandle(HttpServletRequest httpServletRequest,
                           HttpServletResponse httpServletResponse,
                           Object o, ModelAndView modelAndView) throws Exception {

    }

    @Override
    public void afterCompletion(HttpServletRequest httpServletRequest,
                                HttpServletResponse httpServletResponse,
                                Object o, Exception e) throws Exception {
    }

    /**
     * 方法描述 检查token
     *
     * @param handlerMethod
     * @since: 1.0
     * @return: Boolean
     * @author: zyj
     * @date: 2023/4/28 16:21
     */
    public JwtToken checkToken(HandlerMethod handlerMethod) throws TokenException{

        JwtToken jwtToken = null;
        boolean b = false;

        //1: 先查找类上的注解，如果不需要token直接返回
        Class<?> beanType = handlerMethod.getBeanType();
        if (beanType.isAnnotationPresent(JwtToken.class)) {
            jwtToken = beanType.getAnnotation(JwtToken.class);
            if (!jwtToken.token()) {
                b = true;
            }
        }

        //2: 查找方法上的注解，如果不需要token直接返回
        Method method = handlerMethod.getMethod();
        if (method.isAnnotationPresent(JwtToken.class)) {
            jwtToken = method.getAnnotation(JwtToken.class);
            if (!jwtToken.token()) {
                b =  true;
            }
        }

        if(b){
            throw new TokenException(jwt_tishi_code,jwt_tishi);
        }

        return jwtToken;
    }


}
